# @Version        : 1.0
# @Update Time    : 2024/6/18 21:25
# @File           : login_controller.py
# @IDE            : PyCharm
# @Desc           : 登录接口管理
import json
import uuid
from datetime import timedelta

from asyncer import asyncify
from fastapi import APIRouter, Depends, HTTPException, Response
from fastapi.security import OAuth2PasswordRequestForm
from fastapi_utils.cbv import cbv
from sqlalchemy import and_
from sqlalchemy.ext.asyncio import AsyncSession

from authx.core.auth_util import AuthUtil
from authx.core.parameter import LoginParameter
from authx.dependencies.auth import RequireLogin
from authx.models.session import SessionKey
from src.config import settings
from src.core.base_controller import BaseController
from src.core.dependencies import LoginLog
from src.core.exception import BaseAppException
from src.core.response import ResponseSchema
from src.db.db_database import alchemy
from src.db.db_redis import AsyncRedisTool
from src.log import logger
from src.models import SysUser
from src.schemas import DeptUpdate
from src.schemas.system_schema import LoginParams
from src.schemas.user_schema import UserBase, UserUpdatePassword
from src.services.user_service import UserService
from src.utils.captcha import get_captcha, CaptchaType
from src.utils.toolsutils import ToolsUtils

router = APIRouter()


@cbv(router)
class AuthController(BaseController):
    db: AsyncSession = Depends(alchemy.provide_session())
    user_service: UserService = Depends(alchemy.provide_service(UserService))

    async def _authenticate_user(self, username: str, password: str) -> SysUser:
        """
        验证用户身份
        :param username: 用户名
        :param password: 密码
        :return: 用户对象
        """
        user = await self.user_service.get_one_or_none(
            and_(SysUser.username == username)
        )
        if user is None:
            raise BaseAppException(msg="用户不存在")
        if not SysUser.validate_password(password, user.password):
            raise BaseAppException(msg="密码错误")
        if user.status == "2":
            logger.error("用户被禁用")
            raise BaseAppException(msg="用户被禁用")
        return user

    @router.get("/captchaImage", summary="获取图片验证码")
    async def captcha_image(self) -> ResponseSchema:
        """
        获取图片验证码
        """
        img, code = await asyncify(get_captcha)(CaptchaType.TEXT)
        # 生成会话id
        session_id = str(uuid.uuid4())
        await self.request.app.state.redis.set(
            f"{settings.cache.CAPTCHA_KEY}{session_id}", code, ex=timedelta(minutes=2)
        )
        return self.success(data={"img": img, "sessionId": session_id})

    @router.get("/getUserInfo", summary="获取用户信息", description="获取用户信息")
    async def get_current_user_info(
        self, login_id: str = Depends(RequireLogin())
    ) -> ResponseSchema:
        """
        获取用户信息
        :return:
        """
        user = await self.get_current_user(self.db, login_id)
        if not user:
            raise BaseAppException(msg="用户不存在")
        return self.success(
            data={
                "userId": user.id,
                "userName": user.username,
                "phone": user.phone,
                "email": user.email,
                "avatar": user.avatar,
                "nickname": user.nickname,
                "sex": user.sex,
                "remark": user.remark,
                "roles": [r.role_key for r in user.roles],
                "dept": DeptUpdate.model_validate(user.dept).model_dump(),
                "buttons": await AuthUtil.get_permission_list(user.id),
            }
        )

    @router.post("/updateUserInfo", summary="修改个人信息", description="修改个人信息")
    async def update_user_info(
        self, data: UserBase, login_id: str = Depends(RequireLogin())
    ) -> ResponseSchema:
        """
        修改个人信息
        :param login_id:
        :param data:
        :return:
        """
        # 需要判断电话和邮箱是否已经存在
        await self.user_service.update(data=data, item_id=login_id, auto_commit=True)
        return self.success("修改成功")

    @router.post("/updatePassword", summary="修改密码", description="修改密码")
    async def update_password(
        self,
        data: UserUpdatePassword,
        login_id: str = Depends(RequireLogin()),
    ) -> ResponseSchema:
        """
        修改密码
        :param login_id:
        :param data:
        :return:
        """
        user = await self.user_service.get_one_or_none(SysUser.id == login_id)
        if not user:
            raise BaseAppException(msg="用户不存在")
        # 判断密码是否正确
        if not SysUser.validate_password(data.old_password, user.password):
            raise BaseAppException(msg="旧密码错误")
        await self.user_service.update(
            data={"password": SysUser.get_password_hash(data.new_password)},
            item_id=login_id,
            auto_commit=True,
        )
        return self.success("修改成功")

    @router.post("/api/login")
    async def swagger_login(
        self, response: Response, data: OAuth2PasswordRequestForm = Depends()
    ):
        """swagger登录接口

        Args:
            response (Response): _description_
            data (OAuth2PasswordRequestForm, optional): _description_. Defaults to Depends().

        Raises:
            HTTPException: _description_
            HTTPException: _description_

        Returns:
            _type_: _description_
        """
        user = await self._authenticate_user(data.username, data.password)
        # 获取策略
        await AuthUtil.login(response, user.id)

        if user is None:
            raise HTTPException(
                status_code=400,
                detail="用户不存在",
            )
        if user.status == "2":
            logger.error("用户被禁用")
            raise HTTPException(
                status_code=400,
                detail="用户被禁用",
            )
        param = {
            "username": data.username,
            "password": data.password,
        }
        self.request.scope["body"] = json.dumps(param)
        self.request.state.login_type = "1"
        return AuthUtil.result_ok()

    @router.post("/logout", summary="用户注销", description="用户注销")
    async def logout(
        self, response: Response, login_id: str = Depends(RequireLogin())
    ) -> ResponseSchema:
        """
        用户注销
        :param response:
        :param login_id:
        :return:
        """
        # 执行注销操作
        await AuthUtil.logout(response, login_id=login_id)
        return self.success("注销成功")

    @router.post(
        "/login",
        summary="用户登录",
        description="用户登录",
        response_model=ResponseSchema,
        dependencies=[Depends(LoginLog())],
    )
    async def login(
        self,
        response: Response,
        data: LoginParams,
    ):
        """
        用户登录
        :param response:
        :param data:
        :return:
        """
        # 验证码校验
        redis: AsyncRedisTool = self.request.app.state.redis
        code = await redis.get(f"{settings.cache.CAPTCHA_KEY}{data.uuid}")
        if code is None or code != data.code.lower():
            raise BaseAppException(msg="验证码错误")
        user = await self._authenticate_user(data.username, data.password)
        # 获取策略
        login_parameter = LoginParameter.from_config()
        login_parameter.set_extra_data_multi(
            **{
                "ipaddr": getattr(self.request.state, "ipaddr", ""),
                "address": getattr(self.request.state, "address", ""),
                "os": getattr(self.request.state, "os", ""),
                "browser": getattr(self.request.state, "browser", ""),
            }
        )
        await AuthUtil.login(
            response,
            user.id,
            device_type=data.device_type,
            login_parameter=login_parameter,
        )
        # 保存用户信息到redis
        session = await AuthUtil.get_session_by_login_id(user.id)
        await session.set(SessionKey.USER.value, user.to_dict(exclude={"password"}))
        self.request.scope["body"] = json.dumps(
            ToolsUtils.desensitize(data.model_dump())
        )
        self.request.state.login_type = data.method
        self.request.state.username = user.username
        # await self.user_service.update(
        #     {"login_ip": self.request.state.ipaddr}, item_id=user.id, auto_commit=True
        # )
        result = AuthUtil.result_ok()
        return self.success(
            data={
                "token": result.access_token,
                "token_type": result.token_type,
                "refreshToken": result.refresh_token,
            }
        )
